﻿using System.Linq;
using System.Reflection;
using LoungeRepo.Contracts.Core;

namespace LoungeRepo.Normalizer
{
    internal static class EntityRecognizer
    {
        class NonInterfaceEntityIdentity : ILoungeRepoEntityIdentity
        {
            #region Implementation of ILoungeRepoEntityIdentity

            public string Id {get; set;}

            public string Partition { get; set; }

            #endregion
        }


        public static bool TryExtractEntityIdentity(object entityCandidateObject, out ILoungeRepoEntityIdentity entityIdentity)
        {
            if (entityCandidateObject is ILoungeRepoEntity)
            {
                entityIdentity = (entityCandidateObject as ILoungeRepoEntity).GetPersistentId();
            }
            else
            {
                entityIdentity = entityCandidateObject as ILoungeRepoEntityIdentity;
                if (entityIdentity == null)
                {
                    PropertyInfo[] identityProperties =
                        FindEntityIdentityPropertiesByConvention(entityCandidateObject);
                    if (AnyEntityIdentityPropertiesFound(identityProperties))
                        entityIdentity = ExtractEntityIdentityFromProperties(entityCandidateObject, identityProperties);
                }
            }
            return entityIdentity != null;
        }



        private static PropertyInfo[] FindEntityIdentityPropertiesByConvention(object entityCandidateObject)
        {
            return entityCandidateObject.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
                .Where(pi => (pi.Name == "Id" && pi.PropertyType == typeof(string))
                             || (pi.Name == "Partition" && pi.PropertyType == typeof(string)))
                .Select(pi => pi)
                .OrderBy(pi => pi.Name)
                .ToArray();
        }


        private static bool AnyEntityIdentityPropertiesFound(PropertyInfo[] identityProperties)
        {
            return identityProperties.Length > 0 && identityProperties[0].Name == "Id";
        }


        private static ILoungeRepoEntityIdentity ExtractEntityIdentityFromProperties(object entityCandidateObject, PropertyInfo[] identityProperties)
        {
            ILoungeRepoEntityIdentity entityIdentity;
            NonInterfaceEntityIdentity conventionalEntityIdentity;
            {
                conventionalEntityIdentity = new NonInterfaceEntityIdentity { 
                                                    Id = (string)identityProperties[0].GetValue(entityCandidateObject, null), 
                                                    Partition = "" };
                if (identityProperties.Length > 1 && identityProperties[1].Name == "Partition")
                    conventionalEntityIdentity.Partition = (string)identityProperties[1].GetValue(entityCandidateObject, null);
            }
            entityIdentity = conventionalEntityIdentity;
            return entityIdentity;
        }
    }
}
